/*
 * Copyright (c) 2014 University of Washington
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Author: Benjamin Cizdziel <ben.cizdziel@gmail.com>
 */

#include <ns3/geographic-positions.h>
#include <ns3/log.h>
#include <ns3/test.h>

NS_LOG_COMPONENT_DEFINE("GeoToCartesianTest");

using namespace ns3;

// 10 meter tolerance for testing, which is very small with respect to ECEF
// Cartesian coordinates on or above earth's surface
const double TOLERANCE = 10;

const double XSPHERE_MATLAB[216] = {
    6371000,           1968747.27116279,  -5154247.27116279, -5154247.27116279, 1968747.27116279,
    6371000,           1968747.27116279,  608376.364418605,  -1592750,          -1592750.00000000,
    608376.364418605,  1968747.27116279,  -5154247.27116279, -1592750,          4169873.63558139,
    4169873.63558139,  -1592750.00000000, -5154247.27116279, -5154247.27116279, -1592750.00000000,
    4169873.63558139,  4169873.63558140,  -1592750.00000000, -5154247.27116279, 1968747.27116279,
    608376.364418605,  -1592750.00000000, -1592750.00000000, 608376.364418604,  1968747.27116279,
    6371000,           1968747.27116279,  -5154247.27116279, -5154247.27116279, 1968747.27116279,
    6371000,           6371200,           1968809.07456167,  -5154409.07456167, -5154409.07456167,
    1968809.07456166,  6371200,           1968809.07456167,  608395.462719168,  -1592800,
    -1592800.00000000, 608395.462719167,  1968809.07456167,  -5154409.07456167, -1592800,
    4170004.53728083,  4170004.53728083,  -1592800.00000000, -5154409.07456167, -5154409.07456167,
    -1592800,          4170004.53728083,  4170004.53728083,  -1592800.00000000, -5154409.07456167,
    1968809.07456166,  608395.462719167,  -1592800.00000000, -1592800.00000000, 608395.462719167,
    1968809.07456166,  6371200,           1968809.07456167,  -5154409.07456167, -5154409.07456167,
    1968809.07456166,  6371200,           6371400,           1968870.87796054,  -5154570.87796054,
    -5154570.87796054, 1968870.87796054,  6371400,           1968870.87796054,  608414.561019730,
    -1592850,          -1592850.00000000, 608414.561019730,  1968870.87796054,  -5154570.87796054,
    -1592850,          4170135.43898027,  4170135.43898027,  -1592850.00000000, -5154570.87796054,
    -5154570.87796054, -1592850.00000000, 4170135.43898027,  4170135.43898027,  -1592850.00000000,
    -5154570.87796054, 1968870.87796054,  608414.561019730,  -1592850.00000000, -1592850.00000000,
    608414.561019729,  1968870.87796054,  6371400,           1968870.87796054,  -5154570.87796054,
    -5154570.87796054, 1968870.87796054,  6371400,           6371600,           1968932.68135942,
    -5154732.68135941, -5154732.68135942, 1968932.68135941,  6371600,           1968932.68135942,
    608433.659320293,  -1592900,          -1592900.00000000, 608433.659320292,  1968932.68135942,
    -5154732.68135941, -1592900.00000000, 4170266.34067971,  4170266.34067971,  -1592900.00000000,
    -5154732.68135941, -5154732.68135942, -1592900.00000000, 4170266.34067971,  4170266.34067971,
    -1592900.00000000, -5154732.68135942, 1968932.68135941,  608433.659320292,  -1592900.00000000,
    -1592900.00000000, 608433.659320292,  1968932.68135941,  6371600,           1968932.68135942,
    -5154732.68135941, -5154732.68135942, 1968932.68135941,  6371600,           6371800,
    1968994.48475829,  -5154894.48475829, -5154894.48475829, 1968994.48475829,  6371800,
    1968994.48475829,  608452.757620855,  -1592950,          -1592950.00000000, 608452.757620855,
    1968994.48475829,  -5154894.48475829, -1592950,          4170397.24237914,  4170397.24237914,
    -1592950.00000000, -5154894.48475829, -5154894.48475829, -1592950.00000000, 4170397.24237914,
    4170397.24237915,  -1592950.00000000, -5154894.48475829, 1968994.48475829,  608452.757620855,
    -1592950.00000000, -1592950.00000000, 608452.757620854,  1968994.48475829,  6371800,
    1968994.48475829,  -5154894.48475829, -5154894.48475829, 1968994.48475829,  6371800,
    6372000,           1969056.28815717,  -5155056.28815716, -5155056.28815717, 1969056.28815716,
    6372000,           1969056.28815717,  608471.855921418,  -1593000,          -1593000,
    608471.855921417,  1969056.28815717,  -5155056.28815716, -1593000.00000000, 4170528.14407858,
    4170528.14407858,  -1593000.00000000, -5155056.28815716, -5155056.28815717, -1593000.00000000,
    4170528.14407858,  4170528.14407858,  -1593000.00000000, -5155056.28815717, 1969056.28815716,
    608471.855921417,  -1593000.00000000, -1593000.00000000, 608471.855921417,  1969056.28815716,
    6372000,           1969056.28815717,  -5155056.28815716, -5155056.28815717, 1969056.28815716,
    6372000,
};

const double YSPHERE_MATLAB[216] = {
    0,
    6059181.06531642,
    3744779.84235535,
    -3744779.84235535,
    -6059181.06531642,
    -1.56044495147356e-09,
    0,
    1872389.92117767,
    1157200.61148054,
    -1157200.61148054,
    -1872389.92117767,
    -4.82204008791920e-10,
    0,
    -4901980.45383588,
    -3029590.53265821,
    3029590.53265821,
    4901980.45383588,
    1.26242648452870e-09,
    0,
    -4901980.45383589,
    -3029590.53265821,
    3029590.53265821,
    4901980.45383589,
    1.26242648452870e-09,
    0,
    1872389.92117767,
    1157200.61148054,
    -1157200.61148054,
    -1872389.92117767,
    -4.82204008791919e-10,
    0,
    6059181.06531642,
    3744779.84235535,
    -3744779.84235535,
    -6059181.06531642,
    -1.56044495147356e-09,
    0,
    6059371.27661968,
    3744897.39940581,
    -3744897.39940580,
    -6059371.27661968,
    -1.56049393734552e-09,
    0,
    1872448.69970290,
    1157236.93860694,
    -1157236.93860694,
    -1872448.69970290,
    -4.82219146258841e-10,
    0,
    -4902134.33801274,
    -3029685.63830984,
    3029685.63830984,
    4902134.33801274,
    1.26246611493160e-09,
    0,
    -4902134.33801274,
    -3029685.63830984,
    3029685.63830984,
    4902134.33801274,
    1.26246611493160e-09,
    0,
    1872448.69970290,
    1157236.93860694,
    -1157236.93860694,
    -1872448.69970290,
    -4.82219146258841e-10,
    0,
    6059371.27661968,
    3744897.39940581,
    -3744897.39940580,
    -6059371.27661968,
    -1.56049393734552e-09,
    0,
    6059561.48792294,
    3745014.95645626,
    -3745014.95645626,
    -6059561.48792294,
    -1.56054292321749e-09,
    0,
    1872507.47822813,
    1157273.26573334,
    -1157273.26573334,
    -1872507.47822813,
    -4.82234283725763e-10,
    0,
    -4902288.22218960,
    -3029780.74396147,
    3029780.74396147,
    4902288.22218960,
    1.26250574533451e-09,
    0,
    -4902288.22218960,
    -3029780.74396147,
    3029780.74396147,
    4902288.22218960,
    1.26250574533451e-09,
    0,
    1872507.47822813,
    1157273.26573334,
    -1157273.26573334,
    -1872507.47822813,
    -4.82234283725763e-10,
    0,
    6059561.48792294,
    3745014.95645626,
    -3745014.95645626,
    -6059561.48792294,
    -1.56054292321749e-09,
    0,
    6059751.69922620,
    3745132.51350672,
    -3745132.51350672,
    -6059751.69922620,
    -1.56059190908946e-09,
    0,
    1872566.25675336,
    1157309.59285974,
    -1157309.59285974,
    -1872566.25675336,
    -4.82249421192685e-10,
    0,
    -4902442.10636646,
    -3029875.84961310,
    3029875.84961310,
    4902442.10636646,
    1.26254537573741e-09,
    0,
    -4902442.10636646,
    -3029875.84961310,
    3029875.84961310,
    4902442.10636646,
    1.26254537573741e-09,
    0,
    1872566.25675336,
    1157309.59285974,
    -1157309.59285974,
    -1872566.25675336,
    -4.82249421192684e-10,
    0,
    6059751.69922620,
    3745132.51350672,
    -3745132.51350672,
    -6059751.69922620,
    -1.56059190908946e-09,
    0,
    6059941.91052946,
    3745250.07055718,
    -3745250.07055718,
    -6059941.91052946,
    -1.56064089496142e-09,
    0,
    1872625.03527859,
    1157345.91998614,
    -1157345.91998614,
    -1872625.03527859,
    -4.82264558659606e-10,
    0,
    -4902595.99054332,
    -3029970.95526473,
    3029970.95526473,
    4902595.99054332,
    1.26258500614032e-09,
    0,
    -4902595.99054332,
    -3029970.95526473,
    3029970.95526473,
    4902595.99054332,
    1.26258500614032e-09,
    0,
    1872625.03527859,
    1157345.91998614,
    -1157345.91998614,
    -1872625.03527859,
    -4.82264558659606e-10,
    0,
    6059941.91052946,
    3745250.07055718,
    -3745250.07055718,
    -6059941.91052946,
    -1.56064089496142e-09,
    0,
    6060132.12183272,
    3745367.62760764,
    -3745367.62760764,
    -6060132.12183272,
    -1.56068988083339e-09,
    0,
    1872683.81380382,
    1157382.24711254,
    -1157382.24711254,
    -1872683.81380382,
    -4.82279696126528e-10,
    0,
    -4902749.87472018,
    -3030066.06091636,
    3030066.06091636,
    4902749.87472018,
    1.26262463654322e-09,
    0,
    -4902749.87472018,
    -3030066.06091636,
    3030066.06091636,
    4902749.87472018,
    1.26262463654322e-09,
    0,
    1872683.81380382,
    1157382.24711254,
    -1157382.24711254,
    -1872683.81380382,
    -4.82279696126528e-10,
    0,
    6060132.12183272,
    3745367.62760764,
    -3745367.62760764,
    -6060132.12183272,
    -1.56068988083339e-09,
};

const double ZSPHERE_MATLAB[216] = {
    0,
    0,
    0,
    0,
    0,
    0,
    6059181.06531642,
    6059181.06531642,
    6059181.06531642,
    6059181.06531642,
    6059181.06531642,
    6059181.06531642,
    3744779.84235535,
    3744779.84235535,
    3744779.84235535,
    3744779.84235535,
    3744779.84235535,
    3744779.84235535,
    -3744779.84235535,
    -3744779.84235535,
    -3744779.84235535,
    -3744779.84235535,
    -3744779.84235535,
    -3744779.84235535,
    -6059181.06531642,
    -6059181.06531642,
    -6059181.06531642,
    -6059181.06531642,
    -6059181.06531642,
    -6059181.06531642,
    -1.56044495147356e-09,
    -1.56044495147356e-09,
    -1.56044495147356e-09,
    -1.56044495147356e-09,
    -1.56044495147356e-09,
    -1.56044495147356e-09,
    0,
    0,
    0,
    0,
    0,
    0,
    6059371.27661968,
    6059371.27661968,
    6059371.27661968,
    6059371.27661968,
    6059371.27661968,
    6059371.27661968,
    3744897.39940581,
    3744897.39940581,
    3744897.39940581,
    3744897.39940581,
    3744897.39940581,
    3744897.39940581,
    -3744897.39940580,
    -3744897.39940580,
    -3744897.39940580,
    -3744897.39940580,
    -3744897.39940580,
    -3744897.39940580,
    -6059371.27661968,
    -6059371.27661968,
    -6059371.27661968,
    -6059371.27661968,
    -6059371.27661968,
    -6059371.27661968,
    -1.56049393734552e-09,
    -1.56049393734552e-09,
    -1.56049393734552e-09,
    -1.56049393734552e-09,
    -1.56049393734552e-09,
    -1.56049393734552e-09,
    0,
    0,
    0,
    0,
    0,
    0,
    6059561.48792294,
    6059561.48792294,
    6059561.48792294,
    6059561.48792294,
    6059561.48792294,
    6059561.48792294,
    3745014.95645626,
    3745014.95645626,
    3745014.95645626,
    3745014.95645626,
    3745014.95645626,
    3745014.95645626,
    -3745014.95645626,
    -3745014.95645626,
    -3745014.95645626,
    -3745014.95645626,
    -3745014.95645626,
    -3745014.95645626,
    -6059561.48792294,
    -6059561.48792294,
    -6059561.48792294,
    -6059561.48792294,
    -6059561.48792294,
    -6059561.48792294,
    -1.56054292321749e-09,
    -1.56054292321749e-09,
    -1.56054292321749e-09,
    -1.56054292321749e-09,
    -1.56054292321749e-09,
    -1.56054292321749e-09,
    0,
    0,
    0,
    0,
    0,
    0,
    6059751.69922620,
    6059751.69922620,
    6059751.69922620,
    6059751.69922620,
    6059751.69922620,
    6059751.69922620,
    3745132.51350672,
    3745132.51350672,
    3745132.51350672,
    3745132.51350672,
    3745132.51350672,
    3745132.51350672,
    -3745132.51350672,
    -3745132.51350672,
    -3745132.51350672,
    -3745132.51350672,
    -3745132.51350672,
    -3745132.51350672,
    -6059751.69922620,
    -6059751.69922620,
    -6059751.69922620,
    -6059751.69922620,
    -6059751.69922620,
    -6059751.69922620,
    -1.56059190908946e-09,
    -1.56059190908946e-09,
    -1.56059190908946e-09,
    -1.56059190908946e-09,
    -1.56059190908946e-09,
    -1.56059190908946e-09,
    0,
    0,
    0,
    0,
    0,
    0,
    6059941.91052946,
    6059941.91052946,
    6059941.91052946,
    6059941.91052946,
    6059941.91052946,
    6059941.91052946,
    3745250.07055718,
    3745250.07055718,
    3745250.07055718,
    3745250.07055718,
    3745250.07055718,
    3745250.07055718,
    -3745250.07055718,
    -3745250.07055718,
    -3745250.07055718,
    -3745250.07055718,
    -3745250.07055718,
    -3745250.07055718,
    -6059941.91052946,
    -6059941.91052946,
    -6059941.91052946,
    -6059941.91052946,
    -6059941.91052946,
    -6059941.91052946,
    -1.56064089496142e-09,
    -1.56064089496142e-09,
    -1.56064089496142e-09,
    -1.56064089496142e-09,
    -1.56064089496142e-09,
    -1.56064089496142e-09,
    0,
    0,
    0,
    0,
    0,
    0,
    6060132.12183272,
    6060132.12183272,
    6060132.12183272,
    6060132.12183272,
    6060132.12183272,
    6060132.12183272,
    3745367.62760764,
    3745367.62760764,
    3745367.62760764,
    3745367.62760764,
    3745367.62760764,
    3745367.62760764,
    -3745367.62760764,
    -3745367.62760764,
    -3745367.62760764,
    -3745367.62760764,
    -3745367.62760764,
    -3745367.62760764,
    -6060132.12183272,
    -6060132.12183272,
    -6060132.12183272,
    -6060132.12183272,
    -6060132.12183272,
    -6060132.12183272,
    -1.56068988083339e-09,
    -1.56068988083339e-09,
    -1.56068988083339e-09,
    -1.56068988083339e-09,
    -1.56068988083339e-09,
    -1.56068988083339e-09,
};

const double XGRS80_MATLAB[216] = {
    6378137,           1970952.72545164,  -5160021.22545164, -5160021.22545164, 1970952.72545164,
    6378137,           1976947.14312992,  610910.264208145,  -1599383.83577310, -1599383.83577310,
    610910.264208145,  1976947.14312992,  -5165998.77753988, -1596381.41518003, 4179380.80394997,
    4179380.80394997,  -1596381.41518003, -5165998.77753988, -5165998.77753988, -1596381.41518003,
    4179380.80394997,  4179380.80394997,  -1596381.41518003, -5165998.77753988, 1976947.14312991,
    610910.264208145,  -1599383.83577310, -1599383.83577310, 610910.264208145,  1976947.14312991,
    6378137,           1970952.72545164,  -5160021.22545164, -5160021.22545164, 1970952.72545164,
    6378137,           6378337,           1971014.52885052,  -5160183.02885052, -5160183.02885052,
    1971014.52885052,  6378337,           1977008.94652879,  610929.362508708,  -1599433.83577310,
    -1599433.83577310, 610929.362508708,  1977008.94652879,  -5166160.58093876, -1596431.41518003,
    4179511.70564940,  4179511.70564940,  -1596431.41518003, -5166160.58093876, -5166160.58093876,
    -1596431.41518003, 4179511.70564940,  4179511.70564940,  -1596431.41518003, -5166160.58093876,
    1977008.94652879,  610929.362508708,  -1599433.83577310, -1599433.83577310, 610929.362508707,
    1977008.94652879,  6378337,           1971014.52885052,  -5160183.02885052, -5160183.02885052,
    1971014.52885052,  6378337,           6378537,           1971076.33224939,  -5160344.83224939,
    -5160344.83224940, 1971076.33224939,  6378537,           1977070.74992767,  610948.460809271,
    -1599483.83577310, -1599483.83577310, 610948.460809270,  1977070.74992767,  -5166322.38433763,
    -1596481.41518003, 4179642.60734884,  4179642.60734884,  -1596481.41518003, -5166322.38433763,
    -5166322.38433763, -1596481.41518003, 4179642.60734884,  4179642.60734884,  -1596481.41518003,
    -5166322.38433763, 1977070.74992766,  610948.460809270,  -1599483.83577310, -1599483.83577310,
    610948.460809270,  1977070.74992766,  6378537,           1971076.33224939,  -5160344.83224939,
    -5160344.83224940, 1971076.33224939,  6378537,           6378737,           1971138.13564827,
    -5160506.63564827, -5160506.63564827, 1971138.13564827,  6378737,           1977132.55332654,
    610967.559109833,  -1599533.83577310, -1599533.83577310, 610967.559109833,  1977132.55332654,
    -5166484.18773651, -1596531.41518003, 4179773.50904828,  4179773.50904828,  -1596531.41518003,
    -5166484.18773651, -5166484.18773651, -1596531.41518003, 4179773.50904828,  4179773.50904828,
    -1596531.41518003, -5166484.18773651, 1977132.55332654,  610967.559109833,  -1599533.83577310,
    -1599533.83577310, 610967.559109832,  1977132.55332654,  6378737,           1971138.13564827,
    -5160506.63564827, -5160506.63564827, 1971138.13564827,  6378737,           6378937,
    1971199.93904714,  -5160668.43904714, -5160668.43904714, 1971199.93904714,  6378937,
    1977194.35672542,  610986.657410395,  -1599583.83577310, -1599583.83577310, 610986.657410395,
    1977194.35672542,  -5166645.99113538, -1596581.41518003, 4179904.41074772,  4179904.41074772,
    -1596581.41518003, -5166645.99113538, -5166645.99113538, -1596581.41518003, 4179904.41074772,
    4179904.41074772,  -1596581.41518003, -5166645.99113538, 1977194.35672541,  610986.657410395,
    -1599583.83577310, -1599583.83577310, 610986.657410395,  1977194.35672541,  6378937,
    1971199.93904714,  -5160668.43904714, -5160668.43904714, 1971199.93904714,  6378937,
    6379137,           1971261.74244602,  -5160830.24244602, -5160830.24244602, 1971261.74244602,
    6379137,           1977256.16012429,  611005.755710958,  -1599633.83577310, -1599633.83577310,
    611005.755710958,  1977256.16012429,  -5166807.79453425, -1596631.41518003, 4180035.31244715,
    4180035.31244715,  -1596631.41518003, -5166807.79453425, -5166807.79453426, -1596631.41518003,
    4180035.31244715,  4180035.31244715,  -1596631.41518003, -5166807.79453426, 1977256.16012429,
    611005.755710958,  -1599633.83577310, -1599633.83577310, 611005.755710957,  1977256.16012429,
    6379137,           1971261.74244602,  -5160830.24244602, -5160830.24244602, 1971261.74244602,
    6379137,
};

const double YGRS80_MATLAB[216] = {
    0,
    6065968.75567322,
    3748974.86570096,
    -3748974.86570096,
    -6065968.75567322,
    -1.56219301231466e-09,
    0,
    1880188.46284479,
    1162020.37529350,
    -1162020.37529350,
    -1880188.46284479,
    -4.84212398183511e-10,
    0,
    -4913156.80055210,
    -3036497.89479889,
    3036497.89479889,
    4913156.80055210,
    1.26530477346267e-09,
    0,
    -4913156.80055210,
    -3036497.89479889,
    3036497.89479889,
    4913156.80055210,
    1.26530477346267e-09,
    0,
    1880188.46284479,
    1162020.37529350,
    -1162020.37529350,
    -1880188.46284479,
    -4.84212398183511e-10,
    0,
    6065968.75567322,
    3748974.86570096,
    -3748974.86570096,
    -6065968.75567322,
    -1.56219301231466e-09,
    0,
    6066158.96697648,
    3749092.42275142,
    -3749092.42275142,
    -6066158.96697648,
    -1.56224199818663e-09,
    0,
    1880247.24137002,
    1162056.70241990,
    -1162056.70241990,
    -1880247.24137002,
    -4.84227535650433e-10,
    0,
    -4913310.68472896,
    -3036593.00045052,
    3036593.00045051,
    4913310.68472896,
    1.26534440386558e-09,
    0,
    -4913310.68472896,
    -3036593.00045052,
    3036593.00045051,
    4913310.68472896,
    1.26534440386558e-09,
    0,
    1880247.24137002,
    1162056.70241990,
    -1162056.70241990,
    -1880247.24137002,
    -4.84227535650433e-10,
    0,
    6066158.96697648,
    3749092.42275142,
    -3749092.42275142,
    -6066158.96697648,
    -1.56224199818663e-09,
    0,
    6066349.17827974,
    3749209.97980188,
    -3749209.97980187,
    -6066349.17827974,
    -1.56229098405859e-09,
    0,
    1880306.01989525,
    1162093.02954630,
    -1162093.02954630,
    -1880306.01989525,
    -4.84242673117355e-10,
    0,
    -4913464.56890582,
    -3036688.10610215,
    3036688.10610214,
    4913464.56890582,
    1.26538403426848e-09,
    0,
    -4913464.56890582,
    -3036688.10610215,
    3036688.10610215,
    4913464.56890582,
    1.26538403426848e-09,
    0,
    1880306.01989525,
    1162093.02954630,
    -1162093.02954630,
    -1880306.01989525,
    -4.84242673117354e-10,
    0,
    6066349.17827974,
    3749209.97980188,
    -3749209.97980187,
    -6066349.17827974,
    -1.56229098405859e-09,
    0,
    6066539.38958300,
    3749327.53685233,
    -3749327.53685233,
    -6066539.38958300,
    -1.56233996993056e-09,
    0,
    1880364.79842048,
    1162129.35667270,
    -1162129.35667270,
    -1880364.79842048,
    -4.84257810584276e-10,
    0,
    -4913618.45308268,
    -3036783.21175378,
    3036783.21175377,
    4913618.45308268,
    1.26542366467138e-09,
    0,
    -4913618.45308268,
    -3036783.21175378,
    3036783.21175377,
    4913618.45308268,
    1.26542366467138e-09,
    0,
    1880364.79842048,
    1162129.35667270,
    -1162129.35667270,
    -1880364.79842048,
    -4.84257810584276e-10,
    0,
    6066539.38958300,
    3749327.53685233,
    -3749327.53685233,
    -6066539.38958300,
    -1.56233996993056e-09,
    0,
    6066729.60088626,
    3749445.09390279,
    -3749445.09390279,
    -6066729.60088626,
    -1.56238895580252e-09,
    0,
    1880423.57694571,
    1162165.68379910,
    -1162165.68379910,
    -1880423.57694571,
    -4.84272948051198e-10,
    0,
    -4913772.33725954,
    -3036878.31740540,
    3036878.31740540,
    4913772.33725954,
    1.26546329507429e-09,
    0,
    -4913772.33725954,
    -3036878.31740541,
    3036878.31740540,
    4913772.33725954,
    1.26546329507429e-09,
    0,
    1880423.57694571,
    1162165.68379910,
    -1162165.68379910,
    -1880423.57694571,
    -4.84272948051198e-10,
    0,
    6066729.60088626,
    3749445.09390279,
    -3749445.09390279,
    -6066729.60088626,
    -1.56238895580252e-09,
    0,
    6066919.81218952,
    3749562.65095325,
    -3749562.65095325,
    -6066919.81218952,
    -1.56243794167449e-09,
    0,
    1880482.35547094,
    1162202.01092550,
    -1162202.01092550,
    -1880482.35547094,
    -4.84288085518120e-10,
    0,
    -4913926.22143639,
    -3036973.42305703,
    3036973.42305703,
    4913926.22143639,
    1.26550292547719e-09,
    0,
    -4913926.22143639,
    -3036973.42305703,
    3036973.42305703,
    4913926.22143639,
    1.26550292547719e-09,
    0,
    1880482.35547094,
    1162202.01092550,
    -1162202.01092550,
    -1880482.35547094,
    -4.84288085518119e-10,
    0,
    6066919.81218952,
    3749562.65095325,
    -3749562.65095325,
    -6066919.81218952,
    -1.56243794167449e-09,
};

const double ZGRS80_MATLAB[216] = {
    0,
    0,
    0,
    0,
    0,
    0,
    6043686.27213354,
    6043686.27213354,
    6043686.27213354,
    6043686.27213354,
    6043686.27213354,
    6043686.27213354,
    3728191.67572948,
    3728191.67572948,
    3728191.67572948,
    3728191.67572948,
    3728191.67572948,
    3728191.67572948,
    -3728191.67572948,
    -3728191.67572948,
    -3728191.67572948,
    -3728191.67572948,
    -3728191.67572948,
    -3728191.67572948,
    -6043686.27213354,
    -6043686.27213354,
    -6043686.27213354,
    -6043686.27213354,
    -6043686.27213354,
    -6043686.27213354,
    -1.55173509862111e-09,
    -1.55173509862111e-09,
    -1.55173509862111e-09,
    -1.55173509862111e-09,
    -1.55173509862111e-09,
    -1.55173509862111e-09,
    0,
    0,
    0,
    0,
    0,
    0,
    6043876.48343680,
    6043876.48343680,
    6043876.48343680,
    6043876.48343680,
    6043876.48343680,
    6043876.48343680,
    3728309.23277994,
    3728309.23277994,
    3728309.23277994,
    3728309.23277994,
    3728309.23277994,
    3728309.23277994,
    -3728309.23277994,
    -3728309.23277994,
    -3728309.23277994,
    -3728309.23277994,
    -3728309.23277994,
    -3728309.23277994,
    -6043876.48343680,
    -6043876.48343680,
    -6043876.48343680,
    -6043876.48343680,
    -6043876.48343680,
    -6043876.48343680,
    -1.55178408449307e-09,
    -1.55178408449307e-09,
    -1.55178408449307e-09,
    -1.55178408449307e-09,
    -1.55178408449307e-09,
    -1.55178408449307e-09,
    0,
    0,
    0,
    0,
    0,
    0,
    6044066.69474006,
    6044066.69474006,
    6044066.69474006,
    6044066.69474006,
    6044066.69474006,
    6044066.69474006,
    3728426.78983040,
    3728426.78983040,
    3728426.78983040,
    3728426.78983040,
    3728426.78983040,
    3728426.78983040,
    -3728426.78983040,
    -3728426.78983040,
    -3728426.78983040,
    -3728426.78983040,
    -3728426.78983040,
    -3728426.78983040,
    -6044066.69474006,
    -6044066.69474006,
    -6044066.69474006,
    -6044066.69474006,
    -6044066.69474006,
    -6044066.69474006,
    -1.55183307036504e-09,
    -1.55183307036504e-09,
    -1.55183307036504e-09,
    -1.55183307036504e-09,
    -1.55183307036504e-09,
    -1.55183307036504e-09,
    0,
    0,
    0,
    0,
    0,
    0,
    6044256.90604332,
    6044256.90604332,
    6044256.90604332,
    6044256.90604332,
    6044256.90604332,
    6044256.90604332,
    3728544.34688086,
    3728544.34688086,
    3728544.34688086,
    3728544.34688086,
    3728544.34688086,
    3728544.34688086,
    -3728544.34688086,
    -3728544.34688086,
    -3728544.34688086,
    -3728544.34688086,
    -3728544.34688086,
    -3728544.34688086,
    -6044256.90604332,
    -6044256.90604332,
    -6044256.90604332,
    -6044256.90604332,
    -6044256.90604332,
    -6044256.90604332,
    -1.55188205623700e-09,
    -1.55188205623700e-09,
    -1.55188205623700e-09,
    -1.55188205623700e-09,
    -1.55188205623700e-09,
    -1.55188205623700e-09,
    0,
    0,
    0,
    0,
    0,
    0,
    6044447.11734658,
    6044447.11734658,
    6044447.11734658,
    6044447.11734658,
    6044447.11734658,
    6044447.11734658,
    3728661.90393132,
    3728661.90393132,
    3728661.90393132,
    3728661.90393132,
    3728661.90393132,
    3728661.90393132,
    -3728661.90393132,
    -3728661.90393132,
    -3728661.90393132,
    -3728661.90393132,
    -3728661.90393132,
    -3728661.90393132,
    -6044447.11734658,
    -6044447.11734658,
    -6044447.11734658,
    -6044447.11734658,
    -6044447.11734658,
    -6044447.11734658,
    -1.55193104210897e-09,
    -1.55193104210897e-09,
    -1.55193104210897e-09,
    -1.55193104210897e-09,
    -1.55193104210897e-09,
    -1.55193104210897e-09,
    0,
    0,
    0,
    0,
    0,
    0,
    6044637.32864983,
    6044637.32864983,
    6044637.32864983,
    6044637.32864983,
    6044637.32864983,
    6044637.32864983,
    3728779.46098178,
    3728779.46098178,
    3728779.46098178,
    3728779.46098178,
    3728779.46098178,
    3728779.46098178,
    -3728779.46098177,
    -3728779.46098177,
    -3728779.46098177,
    -3728779.46098177,
    -3728779.46098177,
    -3728779.46098177,
    -6044637.32864983,
    -6044637.32864983,
    -6044637.32864983,
    -6044637.32864983,
    -6044637.32864983,
    -6044637.32864983,
    -1.55198002798094e-09,
    -1.55198002798094e-09,
    -1.55198002798094e-09,
    -1.55198002798094e-09,
    -1.55198002798094e-09,
    -1.55198002798094e-09,
};

const double XWGS84_MATLAB[216] = {
    6378137,           1970952.72545164,  -5160021.22545164, -5160021.22545164, 1970952.72545164,
    6378137,           1976947.14310045,  610910.264199039,  -1599383.83574926, -1599383.83574926,
    610910.264199039,  1976947.14310045,  -5165998.77751058, -1596381.41517097, 4179380.80392626,
    4179380.80392626,  -1596381.41517097, -5165998.77751058, -5165998.77751058, -1596381.41517097,
    4179380.80392626,  4179380.80392626,  -1596381.41517097, -5165998.77751058, 1976947.14310045,
    610910.264199039,  -1599383.83574926, -1599383.83574926, 610910.264199039,  1976947.14310045,
    6378137,           1970952.72545164,  -5160021.22545164, -5160021.22545164, 1970952.72545164,
    6378137,           6378337,           1971014.52885052,  -5160183.02885052, -5160183.02885052,
    1971014.52885052,  6378337,           1977008.94649932,  610929.362499602,  -1599433.83574926,
    -1599433.83574926, 610929.362499601,  1977008.94649932,  -5166160.58090945, -1596431.41517097,
    4179511.70562570,  4179511.70562570,  -1596431.41517097, -5166160.58090945, -5166160.58090945,
    -1596431.41517097, 4179511.70562570,  4179511.70562570,  -1596431.41517097, -5166160.58090945,
    1977008.94649932,  610929.362499601,  -1599433.83574926, -1599433.83574926, 610929.362499601,
    1977008.94649932,  6378337,           1971014.52885052,  -5160183.02885052, -5160183.02885052,
    1971014.52885052,  6378337,           6378537,           1971076.33224939,  -5160344.83224939,
    -5160344.83224940, 1971076.33224939,  6378537,           1977070.74989820,  610948.460800164,
    -1599483.83574926, -1599483.83574926, 610948.460800164,  1977070.74989820,  -5166322.38430833,
    -1596481.41517097, 4179642.60732513,  4179642.60732514,  -1596481.41517097, -5166322.38430833,
    -5166322.38430833, -1596481.41517097, 4179642.60732514,  4179642.60732514,  -1596481.41517097,
    -5166322.38430833, 1977070.74989820,  610948.460800164,  -1599483.83574926, -1599483.83574926,
    610948.460800163,  1977070.74989820,  6378537,           1971076.33224939,  -5160344.83224939,
    -5160344.83224940, 1971076.33224939,  6378537,           6378737,           1971138.13564827,
    -5160506.63564827, -5160506.63564827, 1971138.13564827,  6378737,           1977132.55329707,
    610967.559100727,  -1599533.83574926, -1599533.83574926, 610967.559100726,  1977132.55329707,
    -5166484.18770720, -1596531.41517097, 4179773.50902457,  4179773.50902457,  -1596531.41517097,
    -5166484.18770720, -5166484.18770720, -1596531.41517097, 4179773.50902457,  4179773.50902457,
    -1596531.41517097, -5166484.18770720, 1977132.55329707,  610967.559100726,  -1599533.83574926,
    -1599533.83574926, 610967.559100726,  1977132.55329707,  6378737,           1971138.13564827,
    -5160506.63564827, -5160506.63564827, 1971138.13564827,  6378737,           6378937,
    1971199.93904714,  -5160668.43904714, -5160668.43904714, 1971199.93904714,  6378937,
    1977194.35669595,  610986.657401289,  -1599583.83574926, -1599583.83574926, 610986.657401289,
    1977194.35669595,  -5166645.99110608, -1596581.41517097, 4179904.41072401,  4179904.41072401,
    -1596581.41517097, -5166645.99110608, -5166645.99110608, -1596581.41517097, 4179904.41072401,
    4179904.41072401,  -1596581.41517097, -5166645.99110608, 1977194.35669595,  610986.657401289,
    -1599583.83574926, -1599583.83574926, 610986.657401288,  1977194.35669595,  6378937,
    1971199.93904714,  -5160668.43904714, -5160668.43904714, 1971199.93904714,  6378937,
    6379137,           1971261.74244602,  -5160830.24244602, -5160830.24244602, 1971261.74244602,
    6379137,           1977256.16009482,  611005.755701852,  -1599633.83574926, -1599633.83574926,
    611005.755701851,  1977256.16009482,  -5166807.79450495, -1596631.41517097, 4180035.31242345,
    4180035.31242345,  -1596631.41517097, -5166807.79450495, -5166807.79450495, -1596631.41517097,
    4180035.31242345,  4180035.31242345,  -1596631.41517097, -5166807.79450495, 1977256.16009482,
    611005.755701851,  -1599633.83574926, -1599633.83574926, 611005.755701851,  1977256.16009482,
    6379137,           1971261.74244602,  -5160830.24244602, -5160830.24244602, 1971261.74244602,
    6379137,
};

const double YWGS84_MATLAB[216] = {
    0,
    6065968.75567322,
    3748974.86570096,
    -3748974.86570096,
    -6065968.75567322,
    -1.56219301231466e-09,
    0,
    1880188.46281677,
    1162020.37527618,
    -1162020.37527618,
    -1880188.46281677,
    -4.84212398176294e-10,
    0,
    -4913156.80052423,
    -3036497.89478166,
    3036497.89478166,
    4913156.80052423,
    1.26530477345549e-09,
    0,
    -4913156.80052423,
    -3036497.89478166,
    3036497.89478166,
    4913156.80052423,
    1.26530477345549e-09,
    0,
    1880188.46281677,
    1162020.37527618,
    -1162020.37527618,
    -1880188.46281677,
    -4.84212398176293e-10,
    0,
    6065968.75567322,
    3748974.86570096,
    -3748974.86570096,
    -6065968.75567322,
    -1.56219301231466e-09,
    0,
    6066158.96697648,
    3749092.42275142,
    -3749092.42275142,
    -6066158.96697648,
    -1.56224199818663e-09,
    0,
    1880247.24134200,
    1162056.70240258,
    -1162056.70240258,
    -1880247.24134200,
    -4.84227535643215e-10,
    0,
    -4913310.68470109,
    -3036593.00043329,
    3036593.00043329,
    4913310.68470109,
    1.26534440385840e-09,
    0,
    -4913310.68470109,
    -3036593.00043329,
    3036593.00043329,
    4913310.68470109,
    1.26534440385840e-09,
    0,
    1880247.24134200,
    1162056.70240258,
    -1162056.70240258,
    -1880247.24134200,
    -4.84227535643215e-10,
    0,
    6066158.96697648,
    3749092.42275142,
    -3749092.42275142,
    -6066158.96697648,
    -1.56224199818663e-09,
    0,
    6066349.17827974,
    3749209.97980188,
    -3749209.97980187,
    -6066349.17827974,
    -1.56229098405859e-09,
    0,
    1880306.01986723,
    1162093.02952898,
    -1162093.02952898,
    -1880306.01986723,
    -4.84242673110137e-10,
    0,
    -4913464.56887795,
    -3036688.10608492,
    3036688.10608492,
    4913464.56887795,
    1.26538403426130e-09,
    0,
    -4913464.56887795,
    -3036688.10608492,
    3036688.10608492,
    4913464.56887795,
    1.26538403426130e-09,
    0,
    1880306.01986722,
    1162093.02952898,
    -1162093.02952898,
    -1880306.01986723,
    -4.84242673110137e-10,
    0,
    6066349.17827974,
    3749209.97980188,
    -3749209.97980187,
    -6066349.17827974,
    -1.56229098405859e-09,
    0,
    6066539.38958300,
    3749327.53685233,
    -3749327.53685233,
    -6066539.38958300,
    -1.56233996993056e-09,
    0,
    1880364.79839246,
    1162129.35665538,
    -1162129.35665538,
    -1880364.79839246,
    -4.84257810577059e-10,
    0,
    -4913618.45305481,
    -3036783.21173655,
    3036783.21173655,
    4913618.45305481,
    1.26542366466421e-09,
    0,
    -4913618.45305481,
    -3036783.21173655,
    3036783.21173655,
    4913618.45305481,
    1.26542366466421e-09,
    0,
    1880364.79839245,
    1162129.35665538,
    -1162129.35665538,
    -1880364.79839245,
    -4.84257810577058e-10,
    0,
    6066539.38958300,
    3749327.53685233,
    -3749327.53685233,
    -6066539.38958300,
    -1.56233996993056e-09,
    0,
    6066729.60088626,
    3749445.09390279,
    -3749445.09390279,
    -6066729.60088626,
    -1.56238895580252e-09,
    0,
    1880423.57691768,
    1162165.68378178,
    -1162165.68378178,
    -1880423.57691768,
    -4.84272948043980e-10,
    0,
    -4913772.33723167,
    -3036878.31738818,
    3036878.31738818,
    4913772.33723167,
    1.26546329506711e-09,
    0,
    -4913772.33723167,
    -3036878.31738818,
    3036878.31738818,
    4913772.33723167,
    1.26546329506711e-09,
    0,
    1880423.57691768,
    1162165.68378178,
    -1162165.68378178,
    -1880423.57691768,
    -4.84272948043980e-10,
    0,
    6066729.60088626,
    3749445.09390279,
    -3749445.09390279,
    -6066729.60088626,
    -1.56238895580252e-09,
    0,
    6066919.81218952,
    3749562.65095325,
    -3749562.65095325,
    -6066919.81218952,
    -1.56243794167449e-09,
    0,
    1880482.35544291,
    1162202.01090818,
    -1162202.01090818,
    -1880482.35544291,
    -4.84288085510902e-10,
    0,
    -4913926.22140853,
    -3036973.42303981,
    3036973.42303981,
    4913926.22140853,
    1.26550292547002e-09,
    0,
    -4913926.22140853,
    -3036973.42303981,
    3036973.42303981,
    4913926.22140853,
    1.26550292547002e-09,
    0,
    1880482.35544291,
    1162202.01090818,
    -1162202.01090818,
    -1880482.35544291,
    -4.84288085510902e-10,
    0,
    6066919.81218952,
    3749562.65095325,
    -3749562.65095325,
    -6066919.81218952,
    -1.56243794167449e-09,
};

const double ZWGS84_MATLAB[216] = {
    0,
    0,
    0,
    0,
    0,
    0,
    6043686.27224277,
    6043686.27224277,
    6043686.27224277,
    6043686.27224277,
    6043686.27224277,
    6043686.27224277,
    3728191.67583129,
    3728191.67583129,
    3728191.67583129,
    3728191.67583129,
    3728191.67583129,
    3728191.67583129,
    -3728191.67583129,
    -3728191.67583129,
    -3728191.67583129,
    -3728191.67583129,
    -3728191.67583129,
    -3728191.67583129,
    -6043686.27224277,
    -6043686.27224277,
    -6043686.27224277,
    -6043686.27224277,
    -6043686.27224277,
    -6043686.27224277,
    -1.55173509867228e-09,
    -1.55173509867228e-09,
    -1.55173509867228e-09,
    -1.55173509867228e-09,
    -1.55173509867228e-09,
    -1.55173509867228e-09,
    0,
    0,
    0,
    0,
    0,
    0,
    6043876.48354603,
    6043876.48354603,
    6043876.48354603,
    6043876.48354603,
    6043876.48354603,
    6043876.48354603,
    3728309.23288175,
    3728309.23288175,
    3728309.23288175,
    3728309.23288175,
    3728309.23288175,
    3728309.23288175,
    -3728309.23288175,
    -3728309.23288175,
    -3728309.23288175,
    -3728309.23288175,
    -3728309.23288175,
    -3728309.23288175,
    -6043876.48354603,
    -6043876.48354603,
    -6043876.48354603,
    -6043876.48354603,
    -6043876.48354603,
    -6043876.48354603,
    -1.55178408454425e-09,
    -1.55178408454425e-09,
    -1.55178408454425e-09,
    -1.55178408454425e-09,
    -1.55178408454425e-09,
    -1.55178408454425e-09,
    0,
    0,
    0,
    0,
    0,
    0,
    6044066.69484929,
    6044066.69484929,
    6044066.69484929,
    6044066.69484929,
    6044066.69484929,
    6044066.69484929,
    3728426.78993221,
    3728426.78993221,
    3728426.78993221,
    3728426.78993221,
    3728426.78993221,
    3728426.78993221,
    -3728426.78993221,
    -3728426.78993221,
    -3728426.78993221,
    -3728426.78993221,
    -3728426.78993221,
    -3728426.78993221,
    -6044066.69484929,
    -6044066.69484929,
    -6044066.69484929,
    -6044066.69484929,
    -6044066.69484929,
    -6044066.69484929,
    -1.55183307041621e-09,
    -1.55183307041621e-09,
    -1.55183307041621e-09,
    -1.55183307041621e-09,
    -1.55183307041621e-09,
    -1.55183307041621e-09,
    0,
    0,
    0,
    0,
    0,
    0,
    6044256.90615255,
    6044256.90615255,
    6044256.90615255,
    6044256.90615255,
    6044256.90615255,
    6044256.90615255,
    3728544.34698267,
    3728544.34698267,
    3728544.34698267,
    3728544.34698267,
    3728544.34698267,
    3728544.34698267,
    -3728544.34698267,
    -3728544.34698267,
    -3728544.34698267,
    -3728544.34698267,
    -3728544.34698267,
    -3728544.34698267,
    -6044256.90615255,
    -6044256.90615255,
    -6044256.90615255,
    -6044256.90615255,
    -6044256.90615255,
    -6044256.90615255,
    -1.55188205628818e-09,
    -1.55188205628818e-09,
    -1.55188205628818e-09,
    -1.55188205628818e-09,
    -1.55188205628818e-09,
    -1.55188205628818e-09,
    0,
    0,
    0,
    0,
    0,
    0,
    6044447.11745581,
    6044447.11745581,
    6044447.11745581,
    6044447.11745581,
    6044447.11745581,
    6044447.11745581,
    3728661.90403313,
    3728661.90403313,
    3728661.90403313,
    3728661.90403313,
    3728661.90403313,
    3728661.90403313,
    -3728661.90403313,
    -3728661.90403313,
    -3728661.90403313,
    -3728661.90403313,
    -3728661.90403313,
    -3728661.90403313,
    -6044447.11745581,
    -6044447.11745581,
    -6044447.11745581,
    -6044447.11745581,
    -6044447.11745581,
    -6044447.11745581,
    -1.55193104216015e-09,
    -1.55193104216015e-09,
    -1.55193104216015e-09,
    -1.55193104216015e-09,
    -1.55193104216015e-09,
    -1.55193104216015e-09,
    0,
    0,
    0,
    0,
    0,
    0,
    6044637.32875907,
    6044637.32875907,
    6044637.32875907,
    6044637.32875907,
    6044637.32875907,
    6044637.32875907,
    3728779.46108359,
    3728779.46108359,
    3728779.46108359,
    3728779.46108359,
    3728779.46108359,
    3728779.46108359,
    -3728779.46108358,
    -3728779.46108358,
    -3728779.46108358,
    -3728779.46108358,
    -3728779.46108358,
    -3728779.46108358,
    -6044637.32875907,
    -6044637.32875907,
    -6044637.32875907,
    -6044637.32875907,
    -6044637.32875907,
    -6044637.32875907,
    -1.55198002803211e-09,
    -1.55198002803211e-09,
    -1.55198002803211e-09,
    -1.55198002803211e-09,
    -1.55198002803211e-09,
    -1.55198002803211e-09,
};

/**
 * \ingroup mobility
 * \ingroup tests
 * \defgroup mobility-test mobility module tests
 */

/**
 * \ingroup mobility-test
 *
 * \brief Geo To Cartesian Test Case
 *
 * This test verifies the accuracy of the GeographicToCartesianCoordinates()
 * method in the GeographicPositions class, which converts earth
 * geographic/geodetic coordinates to ECEF Cartesian coordinates. To do so, it
 * compares the values generated from the method to values generated from the
 * MATLAB function geodetic2ecef(), which is part of the MATLAB Mapping Toolbox,
 * using the built-in earth referenceSphere, GRS80 referenceEllipsoid, and WGS84
 * referenceEllipsoid in MATLAB. A description of the MATLAB function can be
 * found at this webpage: http://www.mathworks.com/help/map/ref/geodetic2ecef.html
 * Values are compared using 216 test cases for each of the three earth spheroid
 * models.
 */
class GeoToCartesianTestCase : public TestCase
{
  public:
    /**
     * Constructor
     *
     * \param latitude latitude (deg)
     * \param longitude longitude (deg)
     * \param altitude altitude (m)
     * \param sphType sphere type
     * \param i index
     */
    GeoToCartesianTestCase(double latitude,
                           double longitude,
                           double altitude,
                           GeographicPositions::EarthSpheroidType sphType,
                           int i);
    ~GeoToCartesianTestCase() override;

  private:
    void DoRun() override;

    /**
     * Name function
     * \param latitude the latitude (deg)
     * \param longitude the longitude (deg)
     * \param altitude the altitude (m)
     * \param sphType the sphere type
     * \returns the name string
     */
    static std::string Name(double latitude,
                            double longitude,
                            double altitude,
                            GeographicPositions::EarthSpheroidType sphType);
    double m_latitude;                                ///< latitude (deg)
    double m_longitude;                               ///< longitude (deg)
    double m_altitude;                                ///< altitude (m)
    GeographicPositions::EarthSpheroidType m_sphType; ///< spheroid type
    int m_i;                                          ///< index
};

std::string
GeoToCartesianTestCase::Name(double latitude,
                             double longitude,
                             double altitude,
                             GeographicPositions::EarthSpheroidType sphType)
{
    std::ostringstream oss;
    oss << "Geo->Cart: "
        << "LAT-LON-ALT-SPHEROID = " << latitude << " deg - " << longitude << " deg - " << altitude
        << " m - ";
    switch (sphType)
    {
    case GeographicPositions::SPHERE:
        oss << "SPHERE";
        break;
    case GeographicPositions::GRS80:
        oss << "GRS80";
        break;
    case GeographicPositions::WGS84:
        oss << "WGS84";
        break;
    };

    return oss.str();
}

GeoToCartesianTestCase::GeoToCartesianTestCase(double latitude,
                                               double longitude,
                                               double altitude,
                                               GeographicPositions::EarthSpheroidType sphType,
                                               int i)
    : TestCase(Name(latitude, longitude, altitude, sphType)),
      m_latitude(latitude),
      m_longitude(longitude),
      m_altitude(altitude),
      m_sphType(sphType),
      m_i(i)
{
}

GeoToCartesianTestCase::~GeoToCartesianTestCase()
{
}

void
GeoToCartesianTestCase::DoRun()
{
    Vector cart = GeographicPositions::GeographicToCartesianCoordinates(m_latitude,
                                                                        m_longitude,
                                                                        m_altitude,
                                                                        m_sphType);
    if (m_sphType == GeographicPositions::SPHERE)
    {
        NS_TEST_ASSERT_MSG_EQ_TOL(cart.x,
                                  XSPHERE_MATLAB[m_i],
                                  TOLERANCE,
                                  "x coordinate (" << cart.x
                                                   << ") is incorrect "
                                                      "for perfect sphere model in iteration "
                                                   << m_i);
        NS_TEST_ASSERT_MSG_EQ_TOL(cart.y,
                                  YSPHERE_MATLAB[m_i],
                                  TOLERANCE,
                                  "y coordinate (" << cart.y
                                                   << ") is incorrect "
                                                      "for perfect sphere model in iteration "
                                                   << m_i);
        NS_TEST_ASSERT_MSG_EQ_TOL(cart.z,
                                  ZSPHERE_MATLAB[m_i],
                                  TOLERANCE,
                                  "z coordinate (" << cart.z
                                                   << ") is incorrect "
                                                      "for perfect sphere model in iteration "
                                                   << m_i);
    }
    else if (m_sphType == GeographicPositions::GRS80)
    {
        NS_TEST_ASSERT_MSG_EQ_TOL(cart.x,
                                  XGRS80_MATLAB[m_i],
                                  TOLERANCE,
                                  "x coordinate (" << cart.x
                                                   << ") is incorrect "
                                                      "for GRS80 model in iteration "
                                                   << m_i);
        NS_TEST_ASSERT_MSG_EQ_TOL(cart.y,
                                  YGRS80_MATLAB[m_i],
                                  TOLERANCE,
                                  "y coordinate (" << cart.y
                                                   << ") is incorrect "
                                                      "for GRS80 model in iteration "
                                                   << m_i);
        NS_TEST_ASSERT_MSG_EQ_TOL(cart.z,
                                  ZGRS80_MATLAB[m_i],
                                  TOLERANCE,
                                  "z coordinate (" << cart.z
                                                   << ") is incorrect "
                                                      "for GRS80 model in iteration "
                                                   << m_i);
    }
    else // if m_sphType == GeographicPositions::WGS84
    {
        NS_TEST_ASSERT_MSG_EQ_TOL(cart.x,
                                  XWGS84_MATLAB[m_i],
                                  TOLERANCE,
                                  "x coordinate (" << cart.x
                                                   << ") is incorrect "
                                                      "for WGS84 model in iteration "
                                                   << m_i);
        NS_TEST_ASSERT_MSG_EQ_TOL(cart.y,
                                  YWGS84_MATLAB[m_i],
                                  TOLERANCE,
                                  "y coordinate (" << cart.y
                                                   << ") is incorrect "
                                                      "for WGS84 model in iteration "
                                                   << m_i);
        NS_TEST_ASSERT_MSG_EQ_TOL(cart.z,
                                  ZWGS84_MATLAB[m_i],
                                  TOLERANCE,
                                  "z coordinate (" << cart.z
                                                   << ") is incorrect "
                                                      "for WGS84 model in iteration "
                                                   << m_i);
    }
}

/**
 * \ingroup mobility-test
 *
 * \brief Cartesian to Geo Test Case
 *
 * This test verifies the accuracy of the CartesianToGeographicCoordinates()
 * method in the GeographicPositions class, which converts earth
 * ECEF Cartesian coordinates to geographic/geodetic coordinates. To do so, it
 * compares the values generated from the method and converted back to geographic
 * coordinates.  Values are compared using 216 test cases for each of the three
 * earth spheroid models.
 */
class CartesianToGeoTestCase : public TestCase
{
  public:
    /**
     * Constructor
     *
     * \param latitude latitude (deg)
     * \param longitude longitude (deg)
     * \param altitude altitude (m)
     * \param sphType sphere type
     * \param i index
     */
    CartesianToGeoTestCase(double latitude,
                           double longitude,
                           double altitude,
                           GeographicPositions::EarthSpheroidType sphType,
                           int i);
    ~CartesianToGeoTestCase() override;

  private:
    void DoRun() override;

    /**
     * Name function
     * \param latitude the latitude (deg)
     * \param longitude the longitude (deg)
     * \param altitude the altitude (m)
     * \param sphType the sphere type
     * \returns the name string
     */
    static std::string Name(double latitude,
                            double longitude,
                            double altitude,
                            GeographicPositions::EarthSpheroidType sphType);
    double m_latitude;                                ///< latitude (deg)
    double m_longitude;                               ///< longitude (deg)
    double m_altitude;                                ///< altitude (m)
    GeographicPositions::EarthSpheroidType m_sphType; ///< spheroid type
};

std::string
CartesianToGeoTestCase::Name(double latitude,
                             double longitude,
                             double altitude,
                             GeographicPositions::EarthSpheroidType sphType)
{
    std::ostringstream oss;
    oss << "Cart->Geo: "
        << "LAT-LON-ALT-SPHEROID = " << latitude << " deg - " << longitude << " deg - " << altitude
        << " m - ";
    switch (sphType)
    {
    case GeographicPositions::SPHERE:
        oss << "SPHERE";
        break;
    case GeographicPositions::GRS80:
        oss << "GRS80";
        break;
    case GeographicPositions::WGS84:
        oss << "WGS84";
        break;
    };

    return oss.str();
}

CartesianToGeoTestCase::CartesianToGeoTestCase(double latitude,
                                               double longitude,
                                               double altitude,
                                               GeographicPositions::EarthSpheroidType sphType,
                                               int i)
    : TestCase(Name(latitude, longitude, altitude, sphType)),
      m_latitude(latitude),
      m_longitude(longitude),
      m_altitude(altitude),
      m_sphType(sphType)
{
}

CartesianToGeoTestCase::~CartesianToGeoTestCase()
{
}

void
CartesianToGeoTestCase::DoRun()
{
    Vector cart = GeographicPositions::GeographicToCartesianCoordinates(m_latitude,
                                                                        m_longitude,
                                                                        m_altitude,
                                                                        m_sphType);
    Vector geo = GeographicPositions::CartesianToGeographicCoordinates(cart, m_sphType);

    // geographic coords are ambiguous due to angular wrapping, convert to
    // rectangular for comparison
    Vector geocart =
        GeographicPositions::GeographicToCartesianCoordinates(geo.x, geo.y, geo.z, m_sphType);

    NS_TEST_ASSERT_MSG_LT_OR_EQ(CalculateDistance(cart, geocart),
                                2.5, // minimum passing tolerance (m)
                                "Double conversion out-of-tolerance: "
                                    << geo << " <> "
                                    << Vector({m_latitude, m_longitude, m_altitude}));
}

/**
 * \ingroup mobility-test
 *
 * \brief Geo To Cartesian Test Suite
 */
class GeoToCartesianTestSuite : public TestSuite
{
  public:
    GeoToCartesianTestSuite();
};

GeoToCartesianTestSuite::GeoToCartesianTestSuite()
    : TestSuite("geo-to-cartesian", UNIT)
{
    NS_LOG_INFO("creating GeoToCartesianTestSuite");
    int i = 0; // iteration number
    for (double altitude = 0; altitude <= 1000; altitude += 200)
    {
        for (double latitude = 0; latitude <= 360; latitude += 72)
        {
            for (double longitude = 0; longitude <= 360; longitude += 72)
            {
                AddTestCase(new GeoToCartesianTestCase(latitude,
                                                       longitude,
                                                       altitude,
                                                       GeographicPositions::SPHERE,
                                                       i),
                            TestCase::QUICK);
                AddTestCase(new CartesianToGeoTestCase(latitude,
                                                       longitude,
                                                       altitude,
                                                       GeographicPositions::SPHERE,
                                                       i),
                            TestCase::QUICK);
                ++i;
            }
        }
    }
    i = 0;
    for (double altitude = 0; altitude <= 1000; altitude += 200)
    {
        for (double latitude = 0; latitude <= 360; latitude += 72)
        {
            for (double longitude = 0; longitude <= 360; longitude += 72)
            {
                AddTestCase(new GeoToCartesianTestCase(latitude,
                                                       longitude,
                                                       altitude,
                                                       GeographicPositions::GRS80,
                                                       i),
                            TestCase::QUICK);
                AddTestCase(new CartesianToGeoTestCase(latitude,
                                                       longitude,
                                                       altitude,
                                                       GeographicPositions::GRS80,
                                                       i),
                            TestCase::QUICK);
                ++i;
            }
        }
    }
    i = 0;
    for (double altitude = 0; altitude <= 1000; altitude += 200)
    {
        for (double latitude = 0; latitude <= 360; latitude += 72)
        {
            for (double longitude = 0; longitude <= 360; longitude += 72)
            {
                AddTestCase(new GeoToCartesianTestCase(latitude,
                                                       longitude,
                                                       altitude,
                                                       GeographicPositions::WGS84,
                                                       i),
                            TestCase::QUICK);
                AddTestCase(new CartesianToGeoTestCase(latitude,
                                                       longitude,
                                                       altitude,
                                                       GeographicPositions::WGS84,
                                                       i),
                            TestCase::QUICK);
                ++i;
            }
        }
    }
}

/**
 * \ingroup mobility-test
 * Static variable for test initialization
 */
static GeoToCartesianTestSuite g_GeoToCartesianTestSuite;
